package com.unitd.modules.sso.controller;

import com.unitd.frame.comm.utils.BeanUtils;
import com.unitd.frame.comm.utils.IpUtils;
import com.unitd.frame.comm.utils.StringUtils;
import com.unitd.frame.comm.utils.http.HttpUtils;
import com.unitd.frame.spring.helper.EnvironmentHelper;
import com.unitd.frame.springweb.controller.result.AjaxResult;
import com.unitd.frame.sso.annotation.Action;
import com.unitd.frame.sso.annotation.Login;
import com.unitd.frame.sso.annotation.Permission;
import com.unitd.frame.sso.common.config.SSOConfig;
import com.unitd.frame.sso.common.encrypt.Salt;
import com.unitd.frame.sso.common.helper.SSOHelper;
import com.unitd.frame.sso.common.token.SSOToken;
import com.unitd.frame.sso.web.waf.request.WafRequestWrapper;
import com.unitd.modules.comm.exception.ConfigServerException;
import com.unitd.modules.comm.web.BaseController;
import com.unitd.modules.configcenter.entity.Users;
import com.unitd.modules.configcenter.service.IUsersService;
import com.unitd.modules.sso.interceptor.SecurityInterceptor;
import com.unitd.modules.sso.util.SecurityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;

/**
 * @desc 登录控制器, 用户处理用户登录/登出
 * @filename LoginController.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2018/1/27
 */
@RestController
@RequestMapping("/auth")
public class AuthController extends BaseController {

	private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

	/** 锁屏锁定用户标记 */
	private static final String LOCKSCREEN_USER_FLAG = "LockscreenUserFlag";

	@Autowired
	private IUsersService usersService;

	/**
	 * @desc 处理用户登录
	 * @return 响应对象
	 */
	@Login(action = Action.Skip)
	@Permission(action = Action.Skip)
	@RequestMapping(value = "login", method = RequestMethod.POST)
	public AjaxResult doLogin() {
		logger.info("开始登录认证");

		// 获取重定向URL
		String returnUrl = request.getParameter(SSOConfig.getInstance().getParamReturl());

		// 只有post提交的请求才是真正的登录请求,否则get请求指示页面重定向
		if (isPost()) {
			// 获取当前请求Token
			SSOToken token = SSOHelper.getToken(request);

			// 判断是否登录,如果未登录则直接重定向到登录页,否则获取token中用户ID并转到首页
			if(BeanUtils.isNull(token)) {
				// 正常登录 需要过滤 XSS SQL 注入
				WafRequestWrapper wr = new WafRequestWrapper(request);
				String userName = wr.getParameter("userName"); 							// 登录请求中的登录用户名

				if (StringUtils.isNotBlank(userName)) {
					String userPwd = wr.getParameter("userPwd"); 						// 登录密码
					if (StringUtils.isBlank(userPwd)) {
						return new AjaxResult(false, "登录密码不能为空!", returnUrl);
					}

					// 查询用户信息,并验证用户信息的有效性
					Users user = usersService.findByUserName(userName);

					// 账号不存在
					if (user == null) {
						return new AjaxResult(false, "登录用户不存在,请重新输入!", returnUrl);
					}
					// 密码错误(密码利用登录用户名作为动态盐值和输入的密码进行加密后存入数据库中)
					if(!Salt.md5SaltValid(userName, user.getPassword(), userPwd)) {
						return new AjaxResult(false, "账号或密码不匹配,请重新输入!", returnUrl);
					}
					// 账号锁定
					if (user.getStatus() != 1) {
						return new AjaxResult(false, "账号已被锁定,请联系管理员!", returnUrl);
					}

					/* 用户存在,签名合法,登录成功;则设置登录token信息 */
					token = new SSOToken(request, "1000");
					token.setId(user.getId());  											// 实际生产环境中,该ID可以为用户的主键
					token.setUid(user.getId()); 											// 登录用户Id
					token.setUname(user.getName()); 										// 当前的登录用户名
					token.setSuperAdmin(user.getType().intValue()==1); 						// 是否超级管理员
					if(!StringUtils.isNull(user.getGantEnvs())) {
						token.setGantProfiles(new ArrayList<>(Arrays.asList(user.getGantEnvs().split(";|,"))));
					}
					token.setData(user.getName()); 											// 用户名称作为说明信息(这里可以随意组装信息)

					// 记住密码
					if ( "on".equals(wr.getParameter("rememberMe")) ) {
						// 设置登录 Cookie 时长1周 = 604800 秒; 动态设置 maxAge 实现记住密码功能
						request.setAttribute(SSOConfig.SSO_COOKIE_MAXAGE, 604800);
					}
					// 设置登录 Cookie; 最后一个参数为true时添加cookie的同时销毁当前JSESSIONID并重新创建信任的JSESSIONID
					SSOHelper.setSSOCookie(request, response, token, true);
				} else {
					return new AjaxResult(false, "用户名不能为空!", returnUrl);
				}
			}

			// 重定向到指定地址 returnUrl
			if (StringUtils.isBlank(returnUrl)) {
				returnUrl = "/index.html";
			} else {
				returnUrl = HttpUtils.decodeURL(returnUrl);
			}
			return new AjaxResult(true, returnUrl);
		}
		return new AjaxResult(false, returnUrl);
	}

	/**
	 * @desc 统一退出,调用对外提供退出的所有接口
	 * 如果实现了SSOCache 缓存,则SSO组件会自动缓存token当需要推出token时,只需要调用SSOHelper.clearLogin(request, response);方法
	 * 此方法会自动清理token缓存信息,同时各个通过SSO登录了的系统也会自动退出。建议退出登录的时候统一调用此方法;
	 * 如果你不这么干那么你只能挨个不同域退出一遍,最终全站退出。
	 * 如果只是退出登录系统,且不清空缓存,则使用 SSOHelper.logout(request, response);
	 * @return 退出之后的URL
	 */
	@RequestMapping(value = "logout", method = RequestMethod.GET)
	public AjaxResult doLogout() {
		SSOHelper.clearLogin(request, response);
		return new AjaxResult("注销成功!");
	}

	/**
	 * @desc 锁屏及锁屏登录
	 */
	@RequestMapping("lock")
	public AjaxResult doLock() {
		// 登录用户名为空,则说明session中不存在,这时需要去token中获取看是否存在对应的登录用户名信息,如果不存在则直接跳转到登录页面,执行重新登录操作
		SSOToken st = SSOHelper.getToken(request);

		// token不存在,直接重定向到登录页面
		if (BeanUtils.isNull(st)) {
			return new AjaxResult(false, "当前登录已失效, 请重新登录!");
		}

		// token存在,取出token中的登录用户名信息后,清理token缓存信息
		SSOHelper.clearLogin(request, response);
		return new AjaxResult(true, "锁屏成功!");
	}

	/**
	 * @desc 锁屏及锁屏登录
	 */
	@RequestMapping("unlock")
	public AjaxResult unLock(@RequestParam Map<String, String> param) {
		// 前端传递的登录用户名及密码
		String userName = param.get("userName");
		String userPwd = param.get("userPwd");

		if (isPost()) {
			// 锁定页面登录
			Users user = usersService.findByUserName(userName);

			if(BeanUtils.isNull(user)) {
				return new AjaxResult(false, "登录用户不存在,请重新到登录页面登录!", -1);
			} else {
				if (Salt.md5SaltValid(userName, user.getPassword(), userPwd)) {
					// 登录成功,进入后台
					SSOToken st = new SSOToken(request);
					st.setId(user.getId());  											// 实际生产环境中,该ID可以为用户的主键
					st.setUid(user.getId()); 											// 登录用户Id
					st.setUname(user.getName()); 										// 当前的登录用户名
					st.setSuperAdmin(user.getType().intValue()==1); 					// 是否超级管理员
					if(!StringUtils.isNull(user.getGantEnvs())) {
						st.setGantProfiles(new ArrayList<>(Arrays.asList(user.getGantEnvs().split(";|,"))));
					}
					st.setData(user.getName()); 										// 用户名称作为说明信息(这里可以随意组装信息)
					SSOHelper.setSSOCookie(request, response, st, true);

					return new AjaxResult(true, "重新登录成功!");
				} else {
					return new AjaxResult(false, "密码错误,请重新输入!");
				}
			}
		}
		return new AjaxResult(false, "请求方式不对,请联系管理员!");
	}

	/**
	 * @desc 获取当前登录用户信息
	 * @param request http请求
	 * 	 * @return 响应对象
	 */
	@RequestMapping(value = "login_user_info", method = RequestMethod.GET)
	public AjaxResult loginUserInfo(HttpServletRequest request) {
		SSOToken ssoToken = SSOHelper.getToken(request);
		if(BeanUtils.isNull(ssoToken)) {
			return new AjaxResult(401, false, "未登录!", null);
		} else {
			return new AjaxResult(true, null, ssoToken);
		}
	}

	@RequestMapping(value = "update_safe_ipaddr", method = RequestMethod.POST)
	public AjaxResult updateSafeIpaddr(HttpServletRequest request, @RequestParam("authcode") String authcode) {

		if(StringUtils.isNull(authcode)) {
			throw new ConfigServerException(411, "安全码不能为空");
		}

		SecurityUtil.requireSuperAdmin();
		if (!authcode.equals(EnvironmentHelper.getProperty("sensitive.operation.authcode"))) {
			throw new ConfigServerException(411, "安全码错误");
		}

		String ipAddr = IpUtils.getIpAddr(request);
		SecurityInterceptor.setIpWhiteList(ipAddr);

		return new AjaxResult(true, ipAddr, null);
	}


	/**
	 * @desc 注册新用户
	 * @param user 当前注册的用户信息
	 * @return 通用Restful对象
	 */
	@RequestMapping("/register")
	public AjaxResult register(@RequestBody Users user) {
		if (isPost()) {
			Users existUser = usersService.findByUserName(user.getName());
			if(BeanUtils.isNull(existUser)) {

				// 用户名作为密码盐值,与前端输入的密码一起进行盐值加密
				user.setPassword(Salt.md5SaltEncode(user.getName(), user.getPassword()));
				user.setStatus((short) 1); 											// 用户状态(1:正常, 2:失效)
				user.setType((short) 2); 											// 用户类型(非管理员)
				user.setCreatedAt(new Date()); 										// 新增时间
				int rlt = usersService.insertSelective(user);
				if (rlt > 0) {
					// 注册成功,自动登录进入后台
					SSOToken st = new SSOToken(request);
					st.setId(user.getId());
					st.setData(user.getName());
					SSOHelper.setSSOCookie(request, response, st, true);
					return new AjaxResult(true, "注册成功!", st);
				}
			} else {

				return new AjaxResult(true, "注册用户名【" + user.getName() + "】已存在!");
			}
		}
		return new AjaxResult(false, "请求方式不对,请联系管理员!");
	}

	public static void main(String[] args) {
		System.out.println(Salt.md5SaltEncode("admin", "123456"));
	}
}